﻿Imports System.Collections.Generic
Imports System.Runtime.Serialization

<DataContract, Serializable(), CLSCompliant(True)>
Public Class CFilesList : Inherits List(Of CFileNameAndContent)
    Shared Sub New()
        Try
            CProto.Prepare(Of CFilesList)()
        Catch
        End Try
    End Sub

    'Constructors
    Public Sub New()
        MyBase.New
    End Sub
    Public Sub New(count As Integer)
        MyBase.New(count)
    End Sub
    Public Sub New(list As ICollection(Of CFileNameAndContent))
        MyBase.New(list)
    End Sub

    Public Sub New(folderPath As String, Optional exceptExtensions As String() = Nothing, Optional recursive As Boolean = True)
        Me.New(LoadFrom(folderPath, exceptExtensions, recursive))
    End Sub

    Public Shared Function LoadFrom(folderPath As String, Optional exceptExtensions As String() = Nothing, Optional recursive As Boolean = True) As CFilesList
        Return CFolderHash.FromPath(folderPath, exceptExtensions, recursive)
    End Function


	''Override
	Public Overrides Function ToString() As String
		Return String.Concat(CUtilities.CountSummary(Me, "file", "none"), vbTab, " (", CUtilities.FileSize(Me.Total), ")")
	End Function

	'Aggregation
	Public Function MD5() As Guid
        Dim ids As New List(Of Guid)
        For Each i As CFileNameAndContent In Me
            ids.Add(i.Md5)
        Next
        CBinary.MD5_(ids.ToArray)
    End Function
    Public Function Total() As Long
        Dim t As Long = 0
        For Each i As CFileNameAndContent In Me
            If Not IsNothing(i.Content) Then
                t += i.Content.Length
            End If
        Next
        Return t
    End Function
	Public Function TotalFor(names As List(Of String)) As Long
		Dim t As Long = 0
		For Each i As String In names
			Dim f As CFileNameAndContent = GetFile(i)
			If IsNothing(f) Then Continue For
			If IsNothing(f.Content) Then Continue For
			t += f.Content.Length
		Next
		Return t
	End Function


	Private m_names As List(Of String)
	Public ReadOnly Property Names As List(Of String)
		Get
			If IsNothing(m_names) OrElse m_names.Count <> Count Then
				Dim list As New List(Of String)(Index.Keys)
				list.Sort()
				m_names = list
			End If
			Return m_names
		End Get
	End Property
	Private m_hashes As List(Of Guid)
	Public ReadOnly Property Hashes As List(Of Guid)
		Get
			If IsNothing(m_hashes) Then
				Dim list As New List(Of Guid)(Me.Count)
				For Each i As CFileNameAndContent In Me
					list.Add(i.Md5)
				Next
				m_hashes = list
			End If

			Return m_hashes
		End Get
	End Property


	Public Function DetectNew(list As CFilesList) As CFilesList
        Dim temp As New CFilesList
        For Each i As CFileNameAndContent In list
            If Not Me.Has(i.Name) Then temp.Add(i)
        Next
        Return temp
    End Function
    Public Function DetectMissing(list As CFilesList) As CFilesList
        Return list.DetectNew(Me)
    End Function
    Public Function DetectCommon(list As CFilesList) As CFilesList
        Dim temp As New CFilesList
        For Each i As CFileNameAndContent In list
            If Me.Has(i.Name) Then temp.Add(i)
        Next
        Return temp
    End Function
    Public Function DetectDifferent(list As CFilesList) As CFilesList
        Dim temp As New CFilesList
        For Each i As CFileNameAndContent In list
            If Not Me.Has(i.Name) Then Continue For
            If IsNothing(i.Content) Then
                temp.Add(i)
                Continue For
            End If
            Dim j As CFileNameAndContent = Me.GetFile(i.Name)
            If IsNothing(j.Content) Then
                temp.Add(j)
                Continue For
            End If
            If i.Md5 <> j.Md5 Then
                temp.Add(i)
            End If
        Next
        Return temp
    End Function



    Public Function Has(key As String) As Boolean
        Return Index.ContainsKey(key.ToLower)
    End Function
    Public Function GetFile(key As String) As CFileNameAndContent
        Dim c As CFileNameAndContent = Nothing
        Index.TryGetValue(key.ToLower, c)
        Return c
    End Function
    Private m_dict As New Dictionary(Of String, CFileNameAndContent)
    Private ReadOnly Property Index As Dictionary(Of String, CFileNameAndContent)
        Get
            If IsNothing(m_dict) OrElse m_dict.Count <> Me.Count Then
                Dim temp As New Dictionary(Of String, CFileNameAndContent)
                For Each i As CFileNameAndContent In Me
                    temp.Add(i.Name.ToLower, i)
                Next
                m_dict = temp
            End If
            Return m_dict
        End Get
    End Property
End Class
